/*
 * Copyright (c) 2018-2024 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include <utmp.h>
#include <sys/utsname.h>

#include "jm.h"

#if defined(QUICKPOOL_WITH_CUDA)
#include "nvml.h"
#endif

static double sdoublePreTotal = 0, sdoublePrevIdle = 0;
static unsigned long long int slongMaxMem, slongFreeMem;
static unsigned long long int slongMaxSwap, slongFreeSwap;
static unsigned long long int slongPaging;

static int sintNumCpus;

#if defined(QUICKPOOL_WITH_CUDA)
static void *sptrNvmlHandle; 
static bool_t sboolNvmlInitialized; 

static int initialize_nvml_lib(void);
nvmlReturn_t  (*l_nvmlInit)();
nvmlReturn_t  (*l_nvmlShutdown)();
nvmlReturn_t  (*l_nvmlDeviceGetComputeMode)(nvmlDevice_t , nvmlComputeMode_t *);
nvmlReturn_t  (*l_nvmlDeviceGetCount)(unsigned int *);
nvmlReturn_t  (*l_nvmlDeviceGetName)(nvmlDevice_t , char *, unsigned int );
nvmlReturn_t  (*l_nvmlDeviceGetHandleByIndex)(unsigned int , nvmlDevice_t *);
nvmlReturn_t  (*l_nvmlDeviceGetUtilizationRates)(nvmlDevice_t , nvmlUtilization_t *);
nvmlReturn_t  (*l_nvmlDeviceGetMemoryInfo)(nvmlDevice_t , nvmlMemory_t *);
nvmlReturn_t  (*l_nvmlDeviceGetTemperature)(nvmlDevice_t , nvmlTemperatureSensors_t , unsigned int *);
nvmlReturn_t  (*l_nvmlDeviceSetComputeMode)(nvmlDevice_t, nvmlComputeMode_t);
nvmlReturn_t  (*l_nvmlDeviceGetCudaComputeCapability)(nvmlDevice_t, int *, int *);

static unsigned int get_num_gpu_nvidia(void); 
static struct hostGPUList *get_gpuinfo_static_nvidia(void);
static struct hostGPULoad *get_gpuinfo_dynamic_nvidia(void);
#endif

static int get_physical_cores(void);
static FILE *lscpu_popen(const char *, const char *, struct timeval *);
static int lscpu_pclose(FILE *);
static char *keep_alnum(char *);

/**
 * @brief       Get number of cpu/cores of the node.
 *
 * @retval      >0           #1: succeed, number of cpu/cores.
 *
 * @note        Read /proc/cpuinfo to get number of cpus of the node.
 *              If open system file failed, set number of cores to 1.
 *              If SPECIFY_NCPUS is specifed, get number of cores of the node.
 */
int
get_cores(void)
{
    if (0 < sintNumCpus) {
        return sintNumCpus;
    }

    FILE *fp;
    fp = fopen("/proc/cpuinfo", "r");
    if (NULL == fp) {

        log_message(ERR, NOCLASS, "%s: fopen(/proc/cpuinfo) failed due to %m", __func__);
        sintNumCpus = 1;

        return sintNumCpus;
    }

    char strProcessor[BUFSIZ];
    while (fgets(strProcessor, sizeof(strProcessor), fp)) {
        if (0 == strncmp(strProcessor, "processor", 9)) {
            ++ sintNumCpus;
        }
    }
    fclose(fp);

    if (jmParams[SPECIFY_NCPUS].string_value
        && 0 == strcasecmp(jmParams[SPECIFY_NCPUS].string_value, "cores")) {

        int nCores;
	nCores = get_physical_cores();
	if (sintNumCpus < nCores) {
	    sintNumCpus = nCores;
	}
    }

    return sintNumCpus;
} // end function get_cores

/**
 * @brief       Get CPU queue length.
 *
 * @param[out]  r1m          #1: 1 minute of average cpu load
 * @param[out]  r5m          #2: 5 minutes of average cpu load
 * @param[out]  r15m         #3: 15 minutes of average cpu load
 *
 * @retval      0            #1: succeed
 * @retval      -1           #2: failed, fopen or fscan failure
 */
int
get_system_load(double *r1m, double *r5m, double *r15m)
{
    FILE *fp;

    if ((fp = fopen("/proc/loadavg", "r")) == NULL) {
        log_message(ERR, NOCLASS, "%s: fopen(/proc/loadavg) failed due to %m", __func__);
        return -1;
    }

    if (fscanf(fp, "%lf %lf %lf", r1m, r5m, r15m) != 3) {
        log_message(ERR, NOCLASS, "%s: fscanf(/proc/loadavg) failed due to %m", __func__);
        fclose(fp);
        return -1;
    }
    fclose(fp);

    log_message(DEBUG, DEBUGRES, "%s: CPU queue length: r1m=%4.2f, r5m=%4.2f, r15m=%4.2f",
                __func__, *r1m, *r5m, *r15m);

    return 0;
} // end function get_system_load

/**
 * @brief       Get system cpu time.
 *
 * @param[out]  itime        #1: cpu idle time
 * @param[out]  etime        #2: cpu used time
 *
 * @retval      >0.0         #1: succeed
 * @retval      0.0          #2: failed, fopen or fscan failure
 */
double
get_cputime(double *itime, double *etime)
{
    *etime = -1;

    FILE *fp;
    fp = fopen("/proc/stat", "r");
    if (NULL == fp) {
        log_message(ERR, NOCLASS, "%s: fopen(/proc/stat) failed due to %m", __func__);
        return 0.0;
    }

    unsigned long long int user, nice, system, idle;
    unsigned long long int iowait;  //since 2.5.41
    unsigned long long int irq, softirq;    //since 2.6.0
    if (fscanf(fp, "%*s %llu %llu %llu %llu %llu %llu %llu",
               &user, &nice, &system, &idle, &iowait, &irq, &softirq) != 7) {

        log_message(ERR, NOCLASS, "%s: fscanf(/proc/stat) failed due to %m", __func__);
        fclose(fp);

        return 0.0;
    }
    fclose(fp);

    unsigned long long int total;
    total = user + nice + system + idle + iowait + irq + softirq;

    if (0 == sdoublePreTotal) {
        sdoublePrevIdle = idle;
        sdoublePreTotal = total;
        return 0.0;
    }

    *itime = (idle - sdoublePrevIdle);
    sdoublePrevIdle = idle;

    *etime = total - sdoublePreTotal;
    sdoublePreTotal = total;

    float instantUT;
    if (0 < *etime) {

        instantUT = 1.0 - (*itime)/(*etime);

        float k_hz = sysconf(_SC_CLK_TCK);
        (*etime) /= k_hz;
        (*etime) = (*etime)/sintNumCpus;
    } else {
        instantUT = 1.0;
    }

    double cpuUsage;
    smooth(&cpuUsage, instantUT, EXP4);

    return cpuUsage;
} // end function get_cputime

unsigned long long int
get_freemem(void)
{
    /* in kilobytes. */
    return slongFreeMem;
}

unsigned long long int
get_freeswap(void)
{
    /* in kilobytes. */
    return slongFreeSwap;
}

unsigned long long int
get_maxmem(void)
{
    /* in kilobytes. */
    return slongMaxMem;
}

unsigned long long int
get_maxswp(void)
{
    /* in kilobytes. */
    return slongMaxSwap;
}

unsigned long long int
get_availtmp(void)
{
    struct statvfs vstat;
    if (statvfs("/tmp", &vstat) < 0) {
        log_message(ERR, NOCLASS, "%s: statvfs(/tmp) failed due to %m", __func__);
        return 0;
    }

    unsigned long long int freeTmp;
    freeTmp = (float)(vstat.f_bavail) / ((1024 * 1024)/vstat.f_bsize);

    return freeTmp;
} // end function get_availtmp

/**
 * @brief       Get memory paging rate.
 *
 * @param[in]   etime        #2: cpu used time
 *
 * @retval      >0.0         #1: succeed, node memory paging rate
 * @retval      0.0          #2: failed, fopen failure
 */
double
get_paging(double etime)
{
    FILE *fp;
    fp = fopen("/proc/vmstat", "r");
    if (NULL == fp) {
        log_message(ERR, NOCLASS, "%s: fopen(/proc/vmstat) failed due to %m", __func__);
        return 0.0;
    }

    unsigned long long int pageIn = 0;
    unsigned long long int pageOut = 0;

    char strPage[BUFSIZ];
    while (fgets(strPage, sizeof(strPage), fp)) {

        char name[64];
        unsigned long long int value;
        if (sscanf(strPage, "%s %llu", name, &value) != 2) {
            continue;
        }

        if (0 == strcmp(name, "pswpin")) {
            pageIn = value;
        }

        if (0 == strcmp(name, "pswpout")) {
            pageOut = value;
        }

        if (0 != pageIn && 0 != pageOut) {
            break;
        }
    }
    fclose(fp);

    if (0 == slongPaging) {
        slongPaging = pageIn + pageOut;
        return 0.0;
    }

    unsigned long long int page;
    page = pageIn + pageOut;

    double smoothPG;
    if (page < slongPaging) {
        smooth(&smoothPG, (slongPaging - page) / etime, EXP4);
    } else {
        smooth(&smoothPG, (page - slongPaging) / etime, EXP4);
    }
    slongPaging = page;

    log_message(DEBUG, DEBUGRES, "%s: paging=%4.2lf", __func__, smoothPG);

    return smoothPG;
} // end function get_paging

double
iorate(double etime)
{
    return 0.0;
}

/**
 * @brief       Get max memory and free memory.
 *
 * @retval      0            #1: succeed
 * @retval      -1           #2: failed, fopen failure
 *
 * @note        static maxmem, freemem, maxswap and freeswap is set.
 */
int
get_memswap(void)
{
    FILE *fp;
    fp = fopen("/proc/meminfo", "r");
    if (NULL == fp) {
        log_message(ERR, NOCLASS, "%s: open(/proc/meminfo) failed due to %m", __func__);
        slongFreeMem = 0;
        slongFreeSwap = 0;
        return -1;
    }

    unsigned long long int memFree=0, memCached=0, memAvail=0;

    char strMemInfo[BUFSIZ];
    while (fgets(strMemInfo, sizeof(strMemInfo), fp)) {

        char name[64];
        unsigned long long int value;
        if (sscanf(strMemInfo, "%s %llu", name, &value) != 2) {
            continue;
        }

        if (strcmp(name, "MemTotal:") == 0) {
            slongMaxMem = value;
        } else if (strcmp(name, "MemFree:") == 0) {
            memFree = value;
        } else if (strcmp(name, "Cached:") == 0) {
            memCached = value;
        } else if (strcmp(name, "SwapTotal:") == 0) {
            slongMaxSwap = value;
        } else if (strcmp(name, "SwapFree:") == 0) {
            slongFreeSwap = value;
        } else if (strcmp(name, "MemAvailable:") == 0) {
            memAvail = value;
        }
    }
    fclose(fp);

    if (0 != memAvail) { // memAvail is more accurate than memFree+memCached
        slongFreeMem = memAvail;
    } else {
        slongFreeMem = memFree + memCached;
    }

    log_message(DEBUG, DEBUGRES, "%s: max mem=%llu, free mem=%llu, max swap=%llu, free swap=%llu.",
                __func__, slongMaxMem, slongFreeMem, slongMaxSwap, slongFreeSwap);

    return 0;
} // end function get_memswap

/**
 * @brief       Get type of the node.
 *
 * @retval      not NULL     #1: succeed
 * @retval      NULL         #2: failed, calloc failure
 *
 * @note        get host type
 */
char *
get_hosttype(void)
{
    struct utsname buf;

    if (uname(&buf) < 0) {
        return copy_string("unknown");
    }

    unsigned int len;
    len = strlen(buf.sysname) + strlen(buf.machine) + 2;

    char *strHostType;
    strHostType = (char *)calloc(len+1, sizeof(char));
    if (NULL == strHostType) {
        return NULL;
    }

    snprintf(strHostType, len, "%s_%s", buf.sysname, buf.machine);

    return strHostType;
} // end function get_hosttype

/**
 * @brief       Get cpu model of the node.
 *
 * @retval      not NULL     #1: succeed
 * @retval      NULL         #2: failed
 */
char *
get_hostmodel(void)
{
    static char model[MAXNAMELEN];
    int pos = 0;

    model[pos] = '\0';

    FILE *fp;
    fp = fopen("/proc/cpuinfo", "r");
    if (NULL == fp) {
        return model;
    }

    char buf[128], b1[128], b2[128];
    int bmips = 0;
    b1[0] = '\0';
    b2[0] = '\0';

    while (fgets(buf, sizeof(buf) - 1, fp)) {

        if (0 == strncasecmp(buf, "cpu\t", 4)
            || 0 == strncasecmp(buf, "cpu family", 10)) {

            char *p = strchr(buf, ':');
            if (NULL != p) {
                strcpy(b1, keep_alnum(p + 2));
            }
        }

        if (NULL != strstr(buf, "model")) {
            char *p = strchr(buf, ':');
            if (NULL != p) {
                strcpy(b2, keep_alnum(p + 2));
            }
        }

        if (0 == strncasecmp(buf, "bogomips", 8)) {
            char *p = strchr(buf, ':');
            if (p) {
                bmips = atoi(p + 2);
            }
        }
    }

    fclose(fp);

    if (!b1[0]) {
        return model;
    }

    if (isdigit(b1[0])) {
        model[pos++] = 'x';
    }

    strncpy(&model[pos], b1, MAXNAMELEN - 15);
    model[MAXNAMELEN - 15] = '\0';
    pos = strlen(model);
    if (bmips) {
        pos += snprintf(&model[pos], MAXNAMELEN, "_%d", bmips);
        if (b2[0]) {
            model[pos++] = '_';
            strncpy(&model[pos], b2, MAXNAMELEN - pos - 1);
        }
        model[MAXNAMELEN - 1] = '\0';
    }

    return model;
} // end function get_hostmodel

/**
 * @brief       Issue lscpu to get number of cores of the node.
 *
 * @retval      >0           #1: succeed
 * @retval      0            #2: failed, no lscpu or command execute failure
 *
 * @note        get number of cpu cores.
 */
static int
get_physical_cores(void)
{
    int numcores;

    const char *cmd = "/usr/bin/lscpu --parse=core |/bin/grep -v '#'|/bin/sort -n |/usr/bin/uniq|/usr/bin/wc -l";

    struct stat sbuf;
    if (stat("/usr/bin/lscpu", &sbuf) < 0) {
        log_message(ERR, NOCLASS, "%s: stat(/usr/bin/lscpus) failed: %m use cpus from /proc/cpuinfo", __func__);
	return 0;
    }

    struct timeval t;
    t.tv_sec = 5;
    t.tv_usec = 0;

    FILE *fp;
    fp = lscpu_popen(cmd, "r", &t);
    if (NULL == fp) {
        log_message(ERR, NOCLASS, "%s: lscpu_peopen() failed, get no core.", __func__);
        return 0;
    }

    int cc;
    cc = fscanf(fp, "%d", &numcores);
    if (cc != 1) {
        log_message(ERR, NOCLASS, "%s: fscanf() failed, get no core.", __func__);
    }

    lscpu_pclose(fp);

    return numcores;
} // end function get_physical_cores

/**
 * @brief       Get node GPU information.
 *
 * @note        Get number of GPU and its information.
 */
void 
get_gpu_info(void)
{
#if defined(QUICKPOOL_WITH_CUDA)

    if (0 == initialize_nvml_lib()) {

        nvmlReturn_t result;
        result = (*l_nvmlInit)();
        if(NVML_SUCCESS != result) {
            sboolNvmlInitialized = FALSE;
            return;
        }

        sboolNvmlInitialized = TRUE;

        gptrMyHost->num_GPU = get_num_gpu_nvidia();
        get_gpuinfo_static_nvidia();
        get_gpuinfo_dynamic_nvidia();

        result = (*l_nvmlShutdown)();
    }
            
    return;
#else  /* defined(QUICKPOOL_WITH_CUDA) */
    gptrMyHost->num_GPU = 0;
    gptrMyHost->gpuinfo_static = NULL;
    gptrMyHost->gpuinfo_dynamic = NULL;
    return;
#endif  /* defined(QUICKPOOL_WITH_CUDA) */
} // end function get_gpu_info

#if defined(QUICKPOOL_WITH_CUDA)
static int 
initialize_nvml_lib(void)
{
    if (NULL != sptrNvmlHandle) {
        return 0;
    }

    sptrNvmlHandle = dlopen("libnvidia-ml.so", RTLD_LAZY | RTLD_GLOBAL);
    if (dlerror() != NULL) {
        sptrNvmlHandle = NULL;
        return -1;
    }
    
    l_nvmlInit = (nvmlReturn_t (*)())dlsym(sptrNvmlHandle, "nvmlInit");
    l_nvmlShutdown = (nvmlReturn_t (*)())dlsym(sptrNvmlHandle, "nvmlShutdown");
    l_nvmlDeviceGetComputeMode = (nvmlReturn_t (*)(nvmlDevice_t, nvmlComputeMode_t *))dlsym(sptrNvmlHandle, "nvmlDeviceGetComputeMode");
    l_nvmlDeviceGetCount = (nvmlReturn_t (*)(unsigned int *))dlsym(sptrNvmlHandle, "nvmlDeviceGetCount");
    l_nvmlDeviceGetName = (nvmlReturn_t (*)(nvmlDevice_t , char *, unsigned int ))dlsym(sptrNvmlHandle, "nvmlDeviceGetName");
    l_nvmlDeviceGetHandleByIndex = (nvmlReturn_t (*)(unsigned int , nvmlDevice_t *))dlsym(sptrNvmlHandle, "nvmlDeviceGetHandleByIndex");
    l_nvmlDeviceGetUtilizationRates = (nvmlReturn_t  (*)(nvmlDevice_t , nvmlUtilization_t *))dlsym(sptrNvmlHandle, "nvmlDeviceGetUtilizationRates");
    l_nvmlDeviceGetMemoryInfo = (nvmlReturn_t  (*)(nvmlDevice_t , nvmlMemory_t *))dlsym(sptrNvmlHandle, "nvmlDeviceGetMemoryInfo");
    l_nvmlDeviceGetTemperature = (nvmlReturn_t  (*)(nvmlDevice_t , nvmlTemperatureSensors_t , unsigned int *))dlsym(sptrNvmlHandle, "nvmlDeviceGetTemperature");  
    l_nvmlDeviceSetComputeMode = (nvmlReturn_t  (*)(nvmlDevice_t , nvmlComputeMode_t))dlsym(sptrNvmlHandle, "nvmlDeviceSetComputeMode");
    l_nvmlDeviceGetCudaComputeCapability = (nvmlReturn_t (*)(nvmlDevice_t , int *, int *))dlsym(sptrNvmlHandle, "nvmlDeviceGetCudaComputeCapability");

    if (dlerror() != NULL) {
        sptrNvmlHandle = NULL;
        return -1;
    }
        
    return 0;
} // end function initialize_nvml 

bool_t 
isnvml_initialized(void)
{
    return sboolNvmlInitialized;
} 

/**
 * @brief       Get number of nvidia GPU.
 *
 * @retval      >0           #1: succeed
 * @retval      0            #2: failed, initialize nvml lib failure
 */
static unsigned int 
get_num_gpu_nvidia(void)
{
    if (!sboolNvmlInitialized) {
        return 0;
    }

    unsigned int device_count=0;

    nvmlReturn_t result;
    result = (*l_nvmlDeviceGetCount)(&device_count);
    if (NVML_SUCCESS != result) {
        return 0;
    }

    return device_count;
} // end function get_num_gpu_nvidia

/**
 * @brief       Get Nvidia GPU static information.
 *
 * @retval      not NULL     #1: succeed
 * @retval      NULL         #2: failed, initialize nvml lib failure
 *
 * @note        get GPU index, model, maxmem and gpu factor.
 */
static struct hostGPUList *
get_gpuinfo_static_nvidia(void) 
{
    if (!sboolNvmlInitialized) {
        return NULL;
    }

    nvmlReturn_t result;
    nvmlDevice_t device;
    nvmlMemory_t memory;
    nvmlUtilization_t utilization;

    int n;
    int major;
    int minor;
    char name[NVML_DEVICE_NAME_BUFFER_SIZE];
    float factor;

    if (NULL == gptrMyHost->gpuinfo_static && 0 < gptrMyHost->num_GPU) {
        gptrMyHost->gpuinfo_static = (struct hostGPUList *)calloc(gptrMyHost->num_GPU,
                                                              sizeof(struct hostGPUList));
    }

    unsigned int i;
    for (i = 0; i < gptrMyHost->num_GPU; i++) {
    
        result = (*l_nvmlDeviceGetHandleByIndex)(i, &device);
        if (NVML_SUCCESS != result) {
            continue;
        }        

        (*l_nvmlDeviceGetMemoryInfo)(device, &memory);
        //ADD error check;
        (*l_nvmlDeviceGetName)(device, name, NVML_DEVICE_NAME_BUFFER_SIZE);
        (*l_nvmlDeviceGetUtilizationRates)(device, &utilization);
        (*l_nvmlDeviceGetCudaComputeCapability)(device, &major,&minor);
        for (n = 0; n < (int)strlen(name); n++) {
            if (name[n] == ' ') {
                name[n] = '_';
            }
        }

        //ADD error check;
        factor = (float)major + (float)minor/10;

        gptrMyHost->gpuinfo_static[i].gpu_id = i;
        gptrMyHost->gpuinfo_static[i].gpu_model = copy_string(name);
        gptrMyHost->gpuinfo_static[i].gpu_maxmem = (double)memory.total/1024.0f/1024.0f;
        gptrMyHost->gpuinfo_static[i].gpu_factor = factor;             
    }

    return gptrMyHost->gpuinfo_static;
} // end function get_gpuinfo_static_nvidia
 
/**
 * @brief       Get Nvidia GPU dynamic load information.
 *
 * @retval      not NULL     #1: succeed
 * @retval      NULL         #2: failed, initialize nvml lib failure
 *
 * @note        get GPU mode, ut, free memory and temperature.
 */
static struct hostGPULoad *
get_gpuinfo_dynamic_nvidia(void)
{
    if (!sboolNvmlInitialized) {
        return NULL;
    }

    unsigned int temp;
    nvmlReturn_t result;
    nvmlDevice_t device;
    nvmlComputeMode_t compute_mode;
    nvmlMemory_t memory;
    nvmlUtilization_t utilization;

    if (NULL == gptrMyHost->gpuinfo_dynamic && 0 < gptrMyHost->num_GPU) {
        gptrMyHost->gpuinfo_dynamic = (struct hostGPULoad *)calloc(gptrMyHost->num_GPU,
                                                               sizeof(struct hostGPULoad));
    }

    unsigned int i;

    for (i = 0;i < gptrMyHost->num_GPU; i++) {
        result = (*l_nvmlDeviceGetHandleByIndex)(i, &device);
        if (NVML_SUCCESS != result) {
            continue;
        }

        (*l_nvmlDeviceGetComputeMode)(device, &compute_mode);
        (*l_nvmlDeviceGetUtilizationRates)(device, &utilization);
        (*l_nvmlDeviceGetMemoryInfo)(device, &memory);
        (*l_nvmlDeviceGetTemperature)(device, NVML_TEMPERATURE_GPU, &temp);

        /* convert NVML compute mode to unified compute mode */
        int gpuMode = GPU_COMPUTEMODE_DEFAULT;
        switch (compute_mode) {
        case NVML_COMPUTEMODE_DEFAULT:
            gpuMode = GPU_COMPUTEMODE_DEFAULT;
            break;
        case NVML_COMPUTEMODE_EXCLUSIVE_THREAD:
        case NVML_COMPUTEMODE_EXCLUSIVE_PROCESS:
            gpuMode = GPU_COMPUTEMODE_EXCLUSIVE;
            break;
        case NVML_COMPUTEMODE_PROHIBITED:
            gpuMode = GPU_COMPUTEMODE_PROHIBITED;
            break;
        default:
            break;
        }

        gptrMyHost->gpuinfo_dynamic[i].gpu_id = i;
        gptrMyHost->gpuinfo_dynamic[i].gpu_mode = gpuMode;
        FREEUP(gptrMyHost->gpuinfo_dynamic[i].status);
        gptrMyHost->gpuinfo_dynamic[i].status = copy_string("ok");
        gptrMyHost->gpuinfo_dynamic[i].avail_gpu_mem = (double)(memory.free)/1024.0f/1024.0f;
        gptrMyHost->gpuinfo_dynamic[i].gpu_temperature = (int)temp;
        gptrMyHost->gpuinfo_dynamic[i].gpu_ut = utilization.gpu;
        gptrMyHost->gpuinfo_dynamic[i].error = EMPTY_STRING;
    }
 
    return gptrMyHost->gpuinfo_dynamic;
} // end function get_gpuinfo_dynamic_nvidia
#endif

static FILE *
lscpu_popen(const char *cmd, const char *type, struct timeval *t)
{
    FILE *fp;
    struct stat buf;
    int s;
    int cc;
    fd_set rmask;

    if (stat(cmd, &buf) < 0) {
        return NULL;
    }

    fp = popen(cmd, type);
    if (NULL == fp) {
        return NULL;
    }

    s = fileno(fp);
    gf_set_nonblocking(s);

    FD_ZERO(&rmask);
    FD_SET(s, &rmask);

    while (TRUE) {
        cc = select(s + 1, &rmask, NULL, NULL, t);
        if (cc < 0 && errno == EINTR) {
            continue;
        }

        if (0 > cc) {
            pclose(fp);
            fp = NULL;
            break;
        }

        if (0 == cc) {
            pclose(fp);
            fp = NULL;
            break;;
        }
    }

    gf_set_blocking(s);

    return fp;
}

/* lscpu_pclose()
 *
 * Closes a stream opened by lscpu_popen(). If a different
 * FILE object is passed the behaviour is undefined.
 */
static int
lscpu_pclose(FILE *fp)
{
    if (!fp)
        return -1;

    pclose(fp);

    return 0;
}

static char *
keep_alnum(char *str)
{
    char *c = str;
    char *p = str;

    while (*c) {
        if (isalnum((int)*c)) {
            *p++ = *c++;
        } else {
            c++;
        }
    }
    *p = '\0';

    return str;
} // end function keep_alnum
